(() => {
  /* 
  ?.  (可选链操作符)
    语法: 表达式?.xxx  |  表达式?.()
    左侧表达式的值可能是undefined或null, 结果为undefined, 不会抛出错误
  */
  interface State {
    name?: string; // name值为string或undefined
    fn?(): void; // fn值为函数或undefined
  }

  const s: State = {
    name: 'abc',
    fn () {

    }
  }
  s.name?.length
  // if (s.name) {
  //   s.name.length
  // }
  s.name?.indexOf('b')
  s.fn?.()
  // if (s.fn) {
  //   s.fn()
  // }
})();

(() => {
  const person = {
    name: null,
    age: undefined,
  };

  /* 
  ● 语法: 表达式1 ?? 表达式2
  ● 如果左侧表达式1为 null 或 undefined 时，返回右侧表达式2的值，否则返回左侧表达式1的值。
  ● 区别 ||: 左边表达式1对应的boolean值为false时，返回右侧表达式2的值，否则返回表达式1值。
  */
  console.log(person.name ?? 'A') // A
  console.log(person.age ?? 'B') // B
  console.log(false ?? 'C') // false
  console.log(0 ?? 'C') // 0
})();

(() => {
  /* 
  !   非空断言操作符
  ● 语法: 表达式!
  ● 告诉 TS编译器，左侧表达式不会为 null 或 undefined，从而避免编译器报告错误。
  */
  // const div: HTMLElement = document.getElementById('test')  // 可能是null

  const div2: HTMLElement = document.getElementById('test')!
})();

(() => {
  /* 
  ● 语法：表达式!.xxx = value 或 表达式!.xxx()
  ● 断言表达式不会为 null 或 undefined, 整个表达式可以被赋值或调用方法
  ● ?.的表达式只能读取, 不能被赋值
  */

  const div = document.getElementById('test')
  div?.getAttribute('name')
  div!.getAttribute('name')
 
  // div?.innerHTML = 'abc'   // ?.不能进行赋值
  // div!.innerHTML = 'abc' // !.可以进行赋值
  
  /* 
  ?.是安全的操作 => 如果表达式是null或undefined, 就不会进行后续的操作 => 不会报错
  而!.不是安全的 => 如果表达式是null或undefined, 还会进行后续的操作 => 就会报错
  */


})();
